﻿using System;
using Karma.Framework.Core.Metadata.Description;
using log4net;
using Newtonsoft.Json;

namespace Karma.Framework.Core.Modules.Metadata.Converter
{
    public class EntityDescriptionConverter : JsonConverter
    {
        private static ILog LOG = LogManager.GetLogger(typeof(EntityDescriptionConverter).Name);

        public override void WriteJson(JsonWriter writer, object value)
        {
            var description = value as EntityDescription;
            if (description == null)
            {
                throw new ArgumentException("Value debe ser un EntityDescription", "value");
            }
            WriteDescription(writer, description, true);
        }

        private void WriteDescription(JsonWriter writer, EntityDescription description, 
            bool writeInternalDescriptions)
        {
            if (LOG.IsDebugEnabled)
            {
                LOG.Debug(string.Format("Serializing Entity: {0}", new [] {description.Name}));
            }
            writer.WriteStartObject();
            writer.WritePropertyName("Name");
            writer.WriteValue(description.Name);
            // properties
            if (description.Properties != null && description.Properties.Count > 0)
            {
                writer.WritePropertyName("Properties");
                writer.WriteStartArray();
                foreach (var property in description.Properties)
                {
                    writer.WriteStartObject();
                    writer.WritePropertyName("Name");
                    writer.WriteValue(property.Name);
                    writer.WritePropertyName("Type");
                    writer.WriteValue(property.Type);
                    writer.WritePropertyName("IsEntity");
                    writer.WriteValue(property.IsEntity);
                    if (property.IsEntity && writeInternalDescriptions)
                    {
                        writer.WritePropertyName("Description");
                        WriteDescription(writer, property.Description, false);
                    }
                    writer.WriteEndObject();
                }
                writer.WriteEndArray();
            }
            // aggregates
            if (writeInternalDescriptions && description.Aggregates != null && description.Aggregates.Count > 0)
            {
                writer.WritePropertyName("Aggregates");
                writer.WriteStartArray();
                foreach (var aggregate in description.Aggregates)
                {
                    writer.WriteStartObject();
                    writer.WritePropertyName("Name");
                    writer.WriteValue(aggregate.Name);
                    writer.WritePropertyName("TargetType");
                    writer.WriteValue(aggregate.TargetType);
                    writer.WritePropertyName("Description");
                    WriteDescription(writer, aggregate.Description, false);
                    writer.WriteEndObject();
                }
                writer.WriteEndArray();
            }
            // operations
            if (writeInternalDescriptions && description.Operations != null && description.Operations.Count > 0)
            {
                writer.WritePropertyName("Operations");
                writer.WriteStartArray();
                foreach (var operation in description.Operations)
                {
                    writer.WriteStartObject();
                    writer.WritePropertyName("Name");
                    writer.WriteValue(operation.Name);
                    writer.WritePropertyName("ReturnType");
                    writer.WriteValue(operation.ReturnType);
                    if (operation.Parameters != null && operation.Parameters.Count > 0)
                    {
                        writer.WritePropertyName("Parameters");
                        writer.WriteStartArray();
                        foreach (var parameter in operation.Parameters)
                        {
                            writer.WriteStartObject();
                            writer.WritePropertyName("Name");
                            writer.WriteValue(parameter.Name);
                            writer.WritePropertyName("Type");
                            writer.WriteValue(parameter.Type);
                            writer.WritePropertyName("IsEntity");
                            writer.WriteValue(parameter.IsEntity);
                            if (parameter.IsEntity && writeInternalDescriptions)
                            {
                                writer.WritePropertyName("Description");
                                WriteDescription(writer, parameter.Description, false);
                            }
                            writer.WriteEndObject();
                        }
                        writer.WriteEndArray();
                    }
                    writer.WriteEndObject();
                }
                writer.WriteEndArray();
            }
            writer.WriteEndObject();
        }

        public override object ReadJson(JsonReader reader, Type objectType)
        {
            throw new InvalidOperationException();
        }

        public override bool CanConvert(Type objectType)
        {
            return objectType.Equals(typeof(EntityDescription));
        }
    }
}
